package org.icop.model;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Set;

import org.jbpt.hypergraph.abs.IVertex;

public class SimilarityMatrix<V extends IVertex> {

	protected Map<V, Integer> colIds;
	
	protected Map<V, Integer> rowIds;
	
	protected double[][] simMatrix;
	
	public enum AGGREGATION_OPTION {
		MAX, AVG, RelSum
	}
	
	public SimilarityMatrix() {
		this.colIds = new HashMap<>();
		this.rowIds = new HashMap<>();
	}

	public SimilarityMatrix(Set<ValuedCorrespondence<V>> correspondences) {
		this(correspondences, AGGREGATION_OPTION.MAX);
	}

	public SimilarityMatrix(Set<ValuedCorrespondence<V>> correspondences, AGGREGATION_OPTION option) {
		int colId = 0, rowId = 0;
		for (ValuedCorrespondence<V> c : correspondences) {
			for (V v : c.getV1s()) 
				if (!colIds.keySet().contains(v)) 
					colIds.put(v, colId++);
			for (V v : c.getV2s()) 
				if (!rowIds.keySet().contains(v)) 
					rowIds.put(v, rowId++);
		}
		
		this.simMatrix = new double[this.colIds.keySet().size()][this.rowIds.keySet().size()];
		
		for (int i = 0; i < this.simMatrix.length; i++)
			Arrays.fill(this.simMatrix[i], 0.0);
		
		for (V col : this.colIds.keySet()) {
			for (V row : this.rowIds.keySet()) {
				List<Double> simValues = new ArrayList<>();
				for (ValuedCorrespondence<V> c : correspondences) {
					if (c.getV1s().contains(col) && c.getV2s().contains(row))
						simValues.add(c.getConfidenceValue());
				}
				
				this.simMatrix[this.colIds.get(col)][this.rowIds.get(row)] = aggregateSimValues(simValues, option);
			}
		}
		
	}
	
	private double aggregateSimValues(List<Double> values, AGGREGATION_OPTION option) {

		if (values.size() == 0)
			return 0;
		if (values.size() == 1)
			return values.get(0);

		switch (option) {
		case MAX:
			Collections.sort(values);
			return values.get(values.size()-1);
		case AVG:
			double sum = 0;
			for (Double d : values)
				sum += d;
			return sum/((double)values.size());
		case RelSum:
			Collections.sort(values);
			Collections.reverse(values);
			
			double result = values.get(0);
			
			if (result == 1)
				return result;
			
			for(int i = 1; i < values.size(); i++) {
				double diff = 1 - result;
				result += diff * values.get(i);
			}
			return result;
			
		default:
			return 0;
		}
	}

}
